home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
United Public Domain Gold 4
/
United Public Domain Gold 4.iso
/
fredfish
/
ff.0014.dms
/
ff.0014.adf
/
dex
/
dex0.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-04-10
|
24KB
|
1,165 lines
/************************************************************************
* *
* Copyright (c) 1982, Fred Fish *
* All Rights Reserved *
* *
* This software and/or documentation is released for public *
* distribution for personal, non-commercial use only. *
* Limited rights to use, modify, and redistribute are hereby *
* granted for non-commercial purposes, provided that all *
* copyright notices remain intact and all changes are clearly *
* documented. The author makes no warranty of any kind with *
* respect to this product and explicitly disclaims any implied *
* warranties of merchantability or fitness for any particular *
* purpose. *
* *
************************************************************************
*/
/*
* FILE
*
* dex0.c main routines for documentation extraction utility
*
* DESCRIPTION
*
* Contains the main routines and misc utility routines for dex.
*
* FUNCTIONS
*
* main entry point for dex utility
* tolower convert alpha characters to lower case
* options process options list on cmd line
* usage give usage message and abort
* indentation set indentation level
* nofill turn off fill mode
* fill turn on fill mode
* asterisks form box top or bottom
* emit_bline emit cmds for blank line
* set_section lookup and set current section
* reset_section reset current section (no section)
* emit_filled emit text in filled mode
* emit_unfilled emit text in unfilled mode
* set_output switch output stream
*
* AUTHOR
*
* Fred Fish
*
*/
/*
* TOOL
*
* dex documentation extraction utility
*
* SYNOPSIS
*
* dex [-dhtv] [-r rcfile] file1 file2 file3 ...
*
* d => enable debug mode
* h => print internal help message
* t => enable trace mode
* v => enable verbose mode
*
* r => use reconfiguration file <rcfile>
*
* DESCRIPTION
*
* Dex extracts internal documentation from program source
* files, compiling it into a form suitable for input
* to the nroff text formatter. It was developed primarily
* to aid the author in maintaining "up to date" documentation
* for each function in the portable math library (PML).
*
* For further information consult the document
* "DEX - Documentation Extraction Utility".
*
* EXAMPLE
*
* The output of dex is typically collected in one
* or more files to be inserted in the appropriate
* places in other "skeleton" files during nroff
* processing.
*
* For example, in the portable math library usage,
* the following sequence of commands will rebuild
* the library documentation to reflect the current
* state of the library.
*
* cd /usr/public/pml
* dex *
* nroff -mm pml.r >pml.doc
*
* The command "dex *" extracts function and file documentation
* from all the files in the library, and builds two output
* files, "funcs.r" and "files.r". The "funcs.r" file
* contains documentation on each of the library functions.
* The "files.r" file contains documentation on each of the
* files in the portable math library directory. The "pml.r"
* file is an nroff file containing information common
* to all files and functions. At the points where the
* "funcs.r" and "files.r" files are to be inserted, there
* is a ".so <filename>" nroff command.
*
*/
/*
* FILES
*
* ".dexrc" dex reconfiguration file [default]
*
* BUGS
*
* Items are currently collected as encountered in the files
* specified in the command line. A planned future enhancement
* is to sort the collected documentation regions by name.
*
* Lines which being with a "." character, such as the
* line in the "FILES" section above, cause nroff to
* occasionally do unintended things.
*
* The dynamic reconfiguration capability is relatively
* primitive. Once the requirements are more clear,
* a future version will be more flexible.
*
* AUTHOR
*
* Fred Fish
*
*/
#include <stdio.h>
#include "hashtbl.h"
#include "dex.h"
/*
* Some misc global variables.
*/
struct tbl_data *section=NULL; /* Current section being processed */
char *infile = "stdin"; /* Input file name if not stdin */
char *rcfile = ".dexrc"; /* Reconfiguration file */
int filled = TRUE; /* Flag for fill mode on */
int trace = FALSE; /* Trace flag */
int vflag = FALSE; /* Verbose flag */
int debug = FALSE; /* Debug flag */
extern int yydebug; /* YACC internal debug flag */
int indent_level = 0; /* Current indentation level */
int linenum = 1; /* Current input file line number */
FILE *infp = stdin; /* Lexical analysis input stream */
FILE *fp = stdout; /* Current output stream */
char title[128]; /* Title of current region */
/*
* Some misc local variables.
*/
static int suppress = FALSE; /* Flag for region suppression */
static int box_emitted = FALSE; /* Flag for box has been emitted for region */
/*
* The following documentation will be printed out when DEX is
* invoked with the -h argument.
*/
static char *documentation[] = {
"",
"DESCRIPTION",
"",
"\tDex is a program for extracting documentation from program",
"\tsource files, building files suitable for input to a",
"\ttext formatter (such as nroff).",
"",
"USAGE",
"",
"\tdex [-dhtv] [-f rcfile] file1 file2 ...",
"",
"\t\t-d enable debug output (implies -v)",
"\t\t-h print this help info",
"\t\t-t enable trace output",
"\t\t-v verbose mode",
"",
"\t\t-f use specified reconfiguration file",
"\t\t (default \".dexrc\")",
"",
"OUTPUTS",
"",
"\tBy default DEX writes all output to stdout.",
"\tThis can be changed via the reconfiguration file.",
"",
0
};
/*
* FUNCTION
*
* main DEX entry point
*
* KEY WORDS
*
* dex entry point
* main
*
* SYNOPSIS
*
* main(argc,argv)
* int argc;
* char *argv[];
*
* DESCRIPTION
*
* This is where DEX starts executing. All argument list
* switches are processed first, then all the specified
* files are processed.
*
*/
/*
* PSEUDO CODE
*
* Begin main
* Process command line options.
* For each argument list field
* If field was not erased during option processing
* If there is a previously processed file open
* Close the previous file.
* End if
* If the current file cannot be opened then
* Print error message.
* Else
* Save input file name for future use.
* Reset current line number to 1.
* Do any reconfiguration requested.
* If verbose then tell user file name.
* Process the input file.
* Close the input file.
* End if
* End if
* End for
* Exit with "normal" status.
* End main
*
*/
main(argc, argv)
int argc;
char *argv[];
{
FILE *fopen();
char *argp;
int argnum;
options(argc,argv);
for (argnum = 1; argnum < argc; argnum++) {
if ((argp = argv[argnum]) != NULL) {
if (infp != NULL) {
fclose(infp);
}
if ((infp = fopen(argp,"r")) == NULL) {
perror(argp);
} else {
infile = argp;
linenum = 1;
reconfig(argp,rcfile);
if (debug) {dump_tbl();}
if (vflag) {printf("dex: processing \"%s\"\n",argp);}
yyparse();
fclose(infp);
}
}
}
if (infp == stdin) {
linenum = 1;
reconfig(".",rcfile);
yyparse();
}
exit(NORMAL_EXIT);
}
/*
* FUNCTION
*
* tolower force alphabetic characters to lower case
*
* SYNOPSIS
*
* char tolower(ch)
* char ch;
*
* DESCRIPTION
*
* Tolower forces alphabetic upper case characters to lower case.
* If the input character is already lower case, or is not
* alphabetic, then it is simply returned unchanged.
*
* BUGS
*
* Currently works only if native character set is ASCII.
*
*/
/*
* PSEUDO CODE
*
* Begin tolower
* If character is an upper case alphabetic then
* Convert character to lower case and return it.
* Else
* Return character unchanged.
* End if
* End tolower
*
*/
#ifndef tolower
char tolower(ch)
char ch;
{
if ('A' <= ch && ch <= 'Z') {
return(ch + 040);
} else {
return(ch);
}
}
#endif
/*
* FUNCTION
*
* options process command line options
*
* SYNOPSIS
*
* options(argc,argv)
* int argc;
* char *argv[];
*
* DESCRIPTION
*
* Scans argument list, processing each switch as it is
* found. The pointer to each switch string is then
* replaced with a NULL to effectively erase the switch
* argument.
*
*/
/*
* PSEUDO CODE
*
* Begin options
* For each argument in the argument list
* Get pointer to first char of argument.
* If the argument is a switch then
* Replace argument pointer with NULL.
* Look at next argument character.
* While there is another argument character
* Switch on the argument character
* Case "verbose":
* Set verbose flag.
* Break out of switch.
* Case "debug":
* Set debug flag.
* Set YACC debug flag.
* Break out of switch.
* Case "reconfigure":
* If reconfig file is next arg
* If there is no next arg
* Abort with usage message.
* Else
* Save file name.
* If file is actually switch
* Abort with usage message.
* Else
* Erase reconfig switch
* End if
* End if
* Else
* Save pntr to reconfig filename.
* End if
* Break out of switch.
* Case "trace":
* Set trace flag.
* Break out of switch.
* Default:
* Abort with usage message.
* End switch
* End while
* End if
* End for
* End options
*
*/
options(argc, argv)
int argc;
char *argv[];
{
int i;
char c; /* 1st char of current command-line argument */
char *cp; /* current argument pointer */
for (i=1; i<argc; i++) {
cp = argv[i];
if (*cp == '-') {
argv[i] = NULL;
cp++;
while (c = *cp++) {
switch (tolower(c)) {
case 'v':
vflag++;
break;
case 'd':
debug++;
yydebug++;
vflag++;
break;
case 'f':
if (*cp == NULL) {
if (++i >= argc) {
usage(c);
} else {
rcfile = argv[i];
if (*rcfile == '-') {
usage(c);
} else {
argv[i] = NULL;
}
}
} else {
rcfile = cp;
}
break;
case 't':
trace++;
break;
default:
usage(c);
}
}
}
}
}
/*
* FUNCTION
*
* usage give usage message and abort
*
* KEY WORDS
*
* usage
* help processing
* abort locations
*
* SYNOPSIS
*
* usage(ch)
* char ch; (is h for explicit help)
*
* DESCRIPTION
*
* Usage is typically called when a problem has been
* detected in the argument list, or usage help has
* been explicitly requested (via the -h flag).
* It prints an appropriate usage message and then aborts.
*
*/
/*
* PSEUDO CODE
*
* Begin usage
* If character is not the explicit help character then
* Print a brief usage message.
* Print how to get more help.
* Else
* Initialize internal documentation pointer.
* While there is a line of documentation to print
* Print the line of documentation.
* End while
* End if
* Exit with error status.
* End usage
*
*/
usage(c)
char c; /* The char of the option */
{
register char **dp;
if (c != 'h') {
printf("Usage: dex [-dhv] [-f rcfile]\n");
printf(" dex -h for help.\n");
} else {
dp = documentation;
while (*dp != NULL) {
printf("%s\n",*dp++);
}
}
exit(ERROR_EXIT);
}
/*
* FUNCTION
*
* indentation set indentation to specified level
*
* KEY WORDS
*
* indent level
* emit functions
*
* SYNOPSIS
*
* indentation(where)
* int where;
*
* DESCRIPTION
*
* Checks requested indentation level against current
* indentation level. If they do not match then
* emits appropriate nroff command to set indentation
* to the specified level.
*
*/
/*
* PSEUDO CODE
*
* Begin indentation
* If current level is not that requested then
* Emit command to change indentation.
* Update the current indentation level.
* End if
* End indentation
*
*/
indentation(where)
int where;
{
char buffer[16];
if (indent_level != where) {
sprintf(buffer,".in %d",where);
emit(buffer,NULL);
indent_level = where;
}
}
/*
* FUNCTION
*
* nofill if filled mode is on, turn it off
*
* KEY WORDS
*
* fill mode
* emit functions
*
* SYNOPSIS
*
* nofill()
*
* DESCRIPTION
*
* Forces fill mode to be off. If fill mode is currently
* on then emits suitable command to turn it off.
*
*/
/*
* PSEUDO CODE
*
* Begin nofill
* If filled mode is on then
* Emit command to turn it off.
* Remember that it is now off.
* End if
* End nofill
*
*/
nofill()
{
if (filled) {
emit(".nf",NULL);
filled = FALSE;
}
}
/*
* FUNCTION
*
* fill if fill mode is off, turn it on
*
* KEY WORDS
*
* fill mode
* emit functions
*
* SYNOPSIS
*
* fill()
*
* DESCRIPTION
*
* Forces fill mode to be on. If fill mode is currently
* off then emits suitable command to turn it on.
*
*/
/*
* PSEUDO CODE
*
* Begin fill
* If filled mode is off then
* Emit command to turn it on.
* Remember that it is now on.
* End if
* End fill
*
*/
fill()
{
if (!filled) {
emit(".fi",NULL);
filled = TRUE;
}
}
/*
* FUNCTION
*
* asterisks emit command for centered asterisks line
*
* KEY WORDS
*
* emit functions
* box construction
*
* SYNOPSIS
*
* asterisks(how_many)
* int how_many;
*
* DESCRIPTION
*
* Emits a command to center a line with the specified
* number of asterisks. This is used to form the top
* and bottom of a boxed item in the output.
*
*/
/*
* PSEUDO CODE
*
* Begin asterisks
* For the specified number of asterisks
* Accumulate asterisk in buffer.
* End for
* Null terminate buffer string.
* Emit command to cause line to be centered.
* Emit asterisks buffer.
* End asterisks
*
*/
asterisks(how_many)
int how_many;
{
int count;
char buffer[128];
char *bp;
for (bp = buffer, count = 0 ; count < how_many; count++) {
*bp++ = '*';
}
*bp = NULL;
emit(".ce",NULL);
emit(buffer,'\\');
}
/*
* FUNCTION
*
* emit_bline emit command to form blank line
*
* KEY WORDS
*
* blank line
* emit functions
*
* SYNOPSIS
*
* emit_bline()
*
* DESCRIPTION
*
* Emits a suitable command to form a blank line in the
* output. Checks first to verify that output is not
* being suppressed and the the current section is
* enabled for processing. Finally, the current section
* must be enabled for text emission.
*
*/
/*
* PSEUDO CODE
*
* Begin emit_bline
* If there is a current section being processed then
* If output is not suppressed and processing is enabled
* If the section is enabled for text emission then
* Emit command to form blank line.
* End if
* End if
* End if
* End emit_bline
*
*/
emit_bline ()
{
if (section != NULL) {
if (!suppress && (section->flags & PROCESS)) {
if (section->flags & EMITTEXT) {
emit(".sp 1",NULL);
}
}
}
}
/*
* FUNCTION
*
* set_section lookup and set current section
*
* KEY WORDS
*
* emit functions
* section selection
* region suppression
*
* SYNOPSIS
*
* set_section(name)
* char *name;
*
* DESCRIPTION
*
* Looks up the specified section and makes it the
* current section. Resets the region
* "box-emitted" and "suppress processing" flags.
* Also sets the indentation level back to the left
* margin and emits commands to print the section
* name (underlined).
*
*/
/*
* PSEUDO CODE
*
* Begin set_section
* Reset the box emitted flag for region.
* Lookup the section and make it current.
* If section name was found in table then
* If the section begins a region then
* If the section is to be processed
* The region is processed also.
* Switch output stream.
* Emit cmd for indentation.
* Set indentation to zero.
* Emit cmd for filled mode.
* Set filled mode.
* Else
* The region is suppressed.
* End if
* End if
* If not suppressed and can be processed
* Set indentation level back.
* If new page is desired then
* Emit command to start new page.
* End if
* If text emission is enabled then
* If name is to be underlined
* Emit underline command.
* End if
* Emit section name.
* End if
* End if
* End if
* End set_section
*
*/
set_section(name)
char *name;
{
struct tbl_data *tbl_find();
box_emitted = FALSE;
if (debug) {printf("set_section: looking for \"%s\"\n",name);}
section = tbl_find(name);
if (section != NULL) {
if (section->flags & REGION) {
if (debug) {printf("set_section: REGION flag set\n");}
if (section->flags & PROCESS) {
if (debug) {printf("set_section: PROCESS flag set\n");}
suppress = FALSE;
set_output(section);
emit(".in 0",NULL);
indent_level = 0;
emit(".fi",NULL);
filled = TRUE;
} else {
suppress = TRUE;
}
}
if (!suppress && (section->flags & PROCESS)) {
if (debug) {printf("set_section: PROCESS flag set\n");}
indentation(0);
if (section->flags & EMITBP) {
emit(".bp",NULL);
}
if (section->flags & EMITTEXT) {
if (debug) {printf("set_section: EMITTEXT flag set\n");}
if (section->flags & EMITUL) {
emit(".ul 1",NULL);
}
emit(name,'\\');
}
}
}
}
/*
* FUNCTION
*
* reset_section reset current section
*
* KEY WORDS
*
* sections
*
* SYNOPSIS
*
* reset_section();
*
* DESCRIPTION
*
* Resets current section so that there is no section selected.
* This is usually used when a non documentation line
* (I.E code) is encountered. It terminates processing of the
* current section.
*
*/
/*
* PSEUDO CODE
*
* Begin reset_section
* Set current section to NULL.
* End reset section
*
*/
reset_section()
{
section = NULL;
}
/*
* FUNCTION
*
* emit_filled emit text in filled mode
*
* KEY WORDS
*
* emit functions
* text emission
*
* SYNOPSIS
*
* emit_filled(text)
* char *text;
*
* DESCRIPTION
*
* Attempts to emit text in filled mode. If filled
* mode is not enabled for the section then emits
* the text in unfilled mode. Also attempts
* to print the first field of the text in a box,
* if the box output is enabled and has not yet been
* emitted for the current documentation section.
*
*/
/*
* PSEUDO CODE
*
* Begin emit_filled
* If there is a current section then
* If section is not suppressed and can be processed
* If box emission is enabled for section
* If box has not yet been emitted then
* Get string to box.
* Emit command for blank lines.
* Emit commands for box top.
* Emit commands to center string.
* Emit commands for box bottom.
* Emit commands for blank lines.
* Set the box emitted flag.
* End if
* End if
* If text emission is enabled then
* Set indentation level.
* If section is enabled for fill then
* Emit commands for fill.
* Else
* Emit commands for no fill.
* End if
* Emit the text.
* End if
* End if
* End if
* End emit_filled
*
*/
emit_filled(text)
char *text;
{
char buffer[128];
if (section != NULL) {
if (!suppress && (section->flags & PROCESS)) {
if (section->flags & EMITBOX) {
if (!box_emitted) {
xfield(title,text);
emit(".sp 3",NULL);
asterisks(strlen(title)+4);
emit(".ce",NULL);
sprintf(buffer,"* %s *",title);
emit(buffer,'\\');
asterisks(strlen(title)+4);
emit(".sp 3",NULL);
box_emitted = TRUE;
}
}
if (section->flags & EMITTEXT) {
indentation(8);
if (section->flags & EMITFILL) {
fill();
} else {
nofill();
}
emit(text,'\\');
}
}
}
}
/*
* FUNCTION
*
* emit_unfilled emit text in unfilled mode
*
* KEY WORDS
*
* text emission
* emit functions
*
* SYNOPSIS
*
* emit_unfilled(text)
* char *text;
*
* DESCRIPTION
*
* Emits text in unfilled mode providing there is
* a current section, it is not being suppressed,
* and it is enabled for processing and text emission.
*
*/
/*
* PSEUDO CODE
*
* Begin emit_unfilled
* If there is a current section then
* If section is not suppressed and can be processed
* If section is enabled for text emission
* Set nofill mode.
* Set indentation level.
* Emit text.
* End if
* End if
* End if
* End emit_unfilled
*
*/
emit_unfilled(text)
char *text;
{
if (section != NULL) {
if (!suppress && (section->flags & PROCESS)) {
if (section->flags & EMITTEXT) {
nofill();
indentation(16);
emit(text,'\\');
}
}
}
}
/*
* FUNCTION
*
* set_output switch output stream
*
* KEY WORDS
*
* output switching
*
* SYNOPSIS
*
* set_output(region)
* struct tbl_data *region;
*
* DESCRIPTION
*
* Tests to see if the region output has been redirected
* to a file. If so, opens the file if necessary and
* remembers the file pointer in the table entry structure.
*
* If no file is specified then the output goes to the
* standard output.
*
* Initializes the global output pointer to point to
* the stream where output goes.
*
*/
/*
* PSEUDO CODE
*
* Begin set_output
* If the table entry pointer is valid then
* If the region has no output file
* Direct output to standard out.
* Else
* If the file is open then
* Set output to go to file.
* Else
* If file open succeeds
* Remember file pointer.
* Else
* Set output to stdout.
* End if
* End if
* End if
* End if
* End set_output
*
*/
set_output(region)
struct tbl_data *region;
{
if (region != NULL) {
if (region->out == NULL) {
fp = stdout;
} else {
if (region->ofp != NULL) {
fp = region->ofp;
} else {
if ((fp = fopen(region->out,"w")) != NULL) {
region->ofp = fp;
} else {
fp = stdout;
}
}
}
}
}
/*
* FUNCTION
*
* emit do actual output to current output stream
*
* KEY WORDS
*
* I/O functions
* emit
*
* SYNOPSIS
*
* emit(text,quote)
* char *text;
* char quote;
*
* DESCRIPTION
*
* Calls appropriate operating system and/or library
* functions to do the actual I/O. Is localized here
* for both logical and portability reasons.
*
* Emit will automatically append a newline to the output
* of each string. Also, if the quote character is not
* null then all '\' and '.' characters will be preceeded by a
* '\' character.
*
*/
/*
* PSEUDO CODE
*
* Begin emit
* If text pointer is not invalid pointer then
* While there is another character to emit
* If quote flag set and char needs quoting
* Emit quote character.
* End if
* Emit character.
* End while
* Emit newline character.
* End if
* End emit
*
*/
emit(text,quote)
char *text;
char quote;
{
if (text != NULL) {
while (*text != NULL) {
if (quote != NULL && (*text == '.' || *text == '\\')) {
fputc(quote,fp);
}
fputc(*text++,fp);
}
fputc('\n',fp);
}
}
#ifndef unix
perror (err)
char *err;
{
if (err && *err) {
fprintf (stderr, "%s: ", err);
}
fprintf (stderr, "<unknown error!>\n");
}
#endif unix